package com.basker.pisces.domain.command.run;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;

import com.basker.pisces.context.LoginContext;
import com.basker.pisces.context.LoginContextHolder;
import com.basker.pisces.core.meta.IDataObjectMeta;
import com.basker.pisces.core.repository.IDataRepository;
import com.basker.pisces.domain.command.CommandResult;
import com.basker.pisces.domain.command.annotation.Command;
import com.basker.pisces.domain.command.meta.SaveCommand;
import com.basker.pisces.domain.command.option.CommandExecuteOption;
import com.basker.pisces.domain.command.option.SaveCommandExecuteOption;
import com.basker.pisces.domain.property.meta.CreateByProperty;
import com.basker.pisces.domain.property.meta.CreateTimeProperty;
import com.basker.pisces.domain.property.meta.UpdateByProperty;
import com.basker.pisces.domain.property.meta.UpdateTimeProperty;
import com.basker.pisces.domain.validation.validator.ICommandValidator;
import com.basker.pisces.domain.validation.validator.RequiredPropertyValidator;
import com.basker.pisces.domain.validation.validator.StringValueLengthValidator;
import com.basker.pisces.utils.DateUtils;

/**
 * 保存命令的运行器。
 *
 * <li>1.它是一个事务命令，如对应的{@link SaveCommand}没有指定隔离级别，默认它会使用'可重复读的级别'。
 * <li>2.默认提供'必填属性校验器'和'文本长度校验器'(可以关掉)
 *
 * @author hangwen
 */
@Command
public class SaveCommandRunner extends TransactionalCommandRunner<SaveCommand> {

    @Autowired
    private IDataRepository repository;

    @Override
    protected List<ICommandValidator> getInnerValidators() {
        List<ICommandValidator> validators = new ArrayList<>();

        SaveCommand command = this.getCommand();
        if (command.getEnableRequiredPropertyValidator()) {
            boolean disableRequiredPropertyValidator = false;

            CommandExecuteOption option = this.getContext().getOption();
            if (option instanceof SaveCommandExecuteOption) {
                disableRequiredPropertyValidator = ((SaveCommandExecuteOption) option)
                        .isDisableRequiredPropertyValidator();
            }

            if (!disableRequiredPropertyValidator) {
                validators.add(new RequiredPropertyValidator());
            }
        }

        if (command.getEnableStringValueLengthValidator()) {
            boolean disableStringValueLengthValidator = false;

            CommandExecuteOption option = this.getContext().getOption();
            if (option instanceof SaveCommandExecuteOption) {
                disableStringValueLengthValidator = ((SaveCommandExecuteOption) option)
                        .isDisableStringValueLengthValidator();
            }
            if (!disableStringValueLengthValidator) {
                validators.add(new StringValueLengthValidator());
            }
        }

        return validators;
    }

    @Override
    protected void onBeforeTransactionalInvoke(Object[] dataObjects) {
        this.trySetCreateBy(dataObjects);
        this.trySetCreateTime(dataObjects);
        this.trySetUpdateBy(dataObjects);
        this.trySetUpdateTime(dataObjects);
    }

    @Override
    protected CommandResult transactionalInvoke(Object[] dataObjects) {
        IDataObjectMeta objectMeta = this.getContext().getEntity().getDataObjectMeta();
        Object[] idArray = this.repository.saveBatch(objectMeta, dataObjects);

        return CommandResult.ok(Arrays.asList(idArray));
    }

    private void trySetCreateBy(final Object[] dataObjects) {
        CreateByProperty createByProperty = this.getEntity().getCreateByProperty();
        if (createByProperty == null) {
            return;
        }

        LoginContext loginContext = LoginContextHolder.getLoginContext();
        if (loginContext == null) {
            return;
        }

        IDataObjectMeta dataObjectMeta = this.getEntity().getDataObjectMeta();

        for (Object dataObject : dataObjects) {
            if (dataObjectMeta.isFromDB(dataObject)) {
                continue;
            }

            createByProperty.setValue(dataObject, loginContext.getUserId());
        }
    }

    private void trySetCreateTime(Object[] dataObjects) {
        CreateTimeProperty createTimeProperty = this.getEntity().getCreateTimeProperty();
        if (createTimeProperty == null) {
            return;
        }

        IDataObjectMeta dataObjectMeta = this.getEntity().getDataObjectMeta();
        Date now = DateUtils.now();

        for (Object dataObject : dataObjects) {
            if (dataObjectMeta.isFromDB(dataObject)) {
                continue;
            }

            createTimeProperty.setValue(dataObject, now);
        }
    }

    private void trySetUpdateBy(final Object[] dataObjects) {
        UpdateByProperty updateByProperty = this.getEntity().getUpdateByProperty();
        if (updateByProperty == null) {
            return;
        }

        LoginContext loginContext = LoginContextHolder.getLoginContext();
        if (loginContext == null) {
            return;
        }

//        IDataObjectMeta dataObjectMeta = this.getEntity().getDataObjectMeta();

        for (Object dataObject : dataObjects) {
            //不管新增还是更新，都会刷新updateBy
//            if (!dataObjectMeta.isFromDB(dataObject)) {
//                continue;
//            }

            updateByProperty.setValue(dataObject, loginContext.getUserId());
        }
    }

    private void trySetUpdateTime(final Object[] dataObjects) {
        UpdateTimeProperty updateTimeProperty = this.getEntity().getUpdateTimeProperty();
        if (updateTimeProperty == null) {
            return;
        }

//        IDataObjectMeta dataObjectMeta = this.getEntity().getDataObjectMeta();
        Date now = DateUtils.now();

        for (Object dataObject : dataObjects) {
            //不管新增还是更新，都会刷新updateTime
//            if (!dataObjectMeta.isFromDB(dataObject)) {
//                continue;
//            }

            updateTimeProperty.setValue(dataObject, now);
        }
    }

}
